package main

import (
	"fmt"
	"math"
	"time"
)

// 四舍五入函数，保留两位小数
func roundToTwoDecimalPlaces(value float64) float64 {
	return math.Round(value*100) / 100
}

// ChargingStation 结构体
type ChargingStation struct {
	stationID           string
	energyRatePerPeriod [48]float64 // 每30分钟的电费率
	serviceRatePerPeriod [48]float64 // 每30分钟的服务费率
	
}

// NewChargingStation 构造函数
func NewChargingStation(stationID string, energyRates [48]float64, serviceRates [48]float64, timeZone *time.Location) *ChargingStation {
	return &ChargingStation{
		stationID:           stationID,
		energyRatePerPeriod: energyRates,
		serviceRatePerPeriod: serviceRates,
		
	}
}

// 计算某个时间点属于哪一个30分钟时段
func getPeriod(t time.Time) int {
	return t.Hour()*2 + t.Minute()/30
}

// ChargingBill 结构体
type ChargingBill struct {
	StationID        string               `json:"station_id"`
	VehicleID        string               `json:"vehicle_id"`
	StartTime        string               `json:"start_time"`
	EndTime          string               `json:"end_time"`
	TotalEnergyKWh   float64              `json:"total_energy_kwh"`
	TotalEnergyCost  float64              `json:"total_energy_cost"`
	TotalServiceCost float64              `json:"total_service_cost"`
	TotalCost        float64              `json:"total_cost"`
	Details          []PeriodChargingInfo `json:"details"`
}

// PeriodChargingInfo 结构体
type PeriodChargingInfo struct {
	Period      int     `json:"period"`
	EnergyKWh   float64 `json:"energy_kwh"`
	EnergyCost  float64 `json:"energy_cost"`
	ServiceCost float64 `json:"service_cost"`
	TotalCost   float64 `json:"total_cost"`
	TimeZone    string  `json:"time_zone"`
}

// processBilling 生成账单函数
func (cs *ChargingStation) processBilling(vehicleID string, startTime, endTime time.Time, totalEnergyKWh float64) {
	fmt.Println("开始生成账单...")

	// 定义时间格式
	timeFormat := "2006-01-02 15:04:05"

	// 初始化总费用和时间段
	totalEnergyCost := 0.0
	totalServiceCost := 0.0
	totalCost := 0.0
	totalEnergy := 0.0
	parkingFee := 2.5 // 假设停车费固定为 2.5 元
	var details []PeriodChargingInfo // 存储每个时段的详细信息

	// 当前时间
	currentTime := startTime

	// 遍历每个30分钟的时段
	for currentTime.Before(endTime) && totalEnergy < totalEnergyKWh {
		// 计算当前时段
		period := getPeriod(currentTime)

		// 计算当前时段内可以充电的时间
		nextPeriodStart := currentTime.Add(30 * time.Minute).Truncate(30 * time.Minute)
		if nextPeriodStart.After(endTime) {
			nextPeriodStart = endTime
		}

		// 获取当前充电进度下的速率
		chargingRate := getChargingRate(totalEnergy, totalEnergyKWh)

		// 计算在当前时段的充电量（假设充电速率随电量变化）
		chargingDuration := nextPeriodStart.Sub(currentTime).Minutes() / 30.0
		energyThisPeriod := totalEnergyKWh * chargingDuration * chargingRate

		// 防止最后时段充电量超过总量
		if totalEnergy+energyThisPeriod > totalEnergyKWh {
			energyThisPeriod = totalEnergyKWh - totalEnergy
		}

		// 计算当前时段的电费和服务费
		energyCostThisPeriod := roundToTwoDecimalPlaces(energyThisPeriod * cs.energyRatePerPeriod[period])
		serviceCostThisPeriod := roundToTwoDecimalPlaces(energyThisPeriod * cs.serviceRatePerPeriod[period])
		totalCostThisPeriod := roundToTwoDecimalPlaces(energyCostThisPeriod + serviceCostThisPeriod)

		// 累加费用和更新总充电量
		totalEnergyCost += energyCostThisPeriod
		totalServiceCost += serviceCostThisPeriod
		totalCost += totalCostThisPeriod
		totalEnergy += energyThisPeriod

		// 记录当前时段的详细信息，包含时区
		detail := PeriodChargingInfo{
			Period:      period,
			EnergyKWh:   roundToTwoDecimalPlaces(energyThisPeriod),
			EnergyCost:  energyCostThisPeriod,
			ServiceCost: serviceCostThisPeriod,
			TotalCost:   totalCostThisPeriod,
			}
		details = append(details, detail)

		// 移动到下一个时段
		currentTime = nextPeriodStart
	}

	// 生成充电账单
	bill := ChargingBill{
		StationID:        cs.stationID,
		VehicleID:        vehicleID,
		StartTime:        startTime.Format(timeFormat), // 格式化时间
		EndTime:          endTime.Format(timeFormat),   // 格式化时间
		TotalEnergyKWh:   roundToTwoDecimalPlaces(totalEnergy),
		TotalEnergyCost:  roundToTwoDecimalPlaces(totalEnergyCost),
		TotalServiceCost: roundToTwoDecimalPlaces(totalServiceCost),
		TotalCost:        roundToTwoDecimalPlaces(totalCost + parkingFee), // 总费用包含停车费
		Details:          details,
	}

	// 输出账单信息
	cs.uploadChargingInfo(bill)
}

// 模拟上传充电账单信息至后台
func (cs *ChargingStation) uploadChargingInfo(bill ChargingBill) {
	// 模拟输出账单详细信息
	fmt.Printf("账单生成成功：\n站点ID: %s\n车辆ID: %s\n总充电量: %.2f kWh\n总电费: %.2f 元\n总服务费: %.2f 元\n总费用: %.2f 元\n",
		bill.StationID, bill.VehicleID, bill.TotalEnergyKWh, bill.TotalEnergyCost, bill.TotalServiceCost, bill.TotalCost)

	fmt.Println("每个时段的详细费用如下:")
	for _, detail := range bill.Details {
		fmt.Printf("时段 %d: %.2f kWh, 电费 %.2f 元, 服务费 %.2f 元, 总费用 %.2f 元\n",
			detail.Period, detail.EnergyKWh, detail.EnergyCost, detail.ServiceCost, detail.TotalCost)
	}
}

// 模拟充电速率变化
func getChargingRate(energyFilled float64, totalEnergyKWh float64) float64 {
	// 例如：充电的前70%速率为固定值，最后30%逐渐减缓
	if energyFilled/totalEnergyKWh < 0.7 {
		return 1.0 // 速率保持不变
	} else {
		return 0.7 // 最后30%速率降低
	}
}



func main() {
	// 创建一个充电桩
	energyRates := [48]float64{
		1.0, 1.0, 0.8, 0.8, 0.7, 0.7, // 00:00 - 03:00
		0.6, 0.6, 0.7, 0.7, 1.0, 1.0, // 03:00 - 06:00
		1.5, 1.5, 1.8, 1.8, 1.5, 1.5, // 06:00 - 09:00
		1.2, 1.2, 1.0, 1.0, 0.8, 0.8, // 09:00 - 12:00
		0.7, 0.7, 0.6, 0.6, 0.7, 0.7, // 12:00 - 15:00
		0.8, 0.8, 1.0, 1.0, 1.2, 1.2, // 15:00 - 18:00
		1.5, 1.5, 1.8, 1.8, 1.5, 1.5, // 18:00 - 21:00
		1.2, 1.2, 1.0, 1.0, 0.8, 0.8, // 21:00 - 24:00
	}
	serviceRates := [48]float64{
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
		0.1, 0.1, 0.1, 0.1,
	}
	timeZone, _ := time.LoadLocation("Asia/Shanghai") // 设置时区为上海

	chargingStation := NewChargingStation("CS001", energyRates, serviceRates, timeZone)

	// 定义充电开始和结束时间
	startTime := time.Date(2024, 10, 11, 14, 0, 0, 0, timeZone) // 14:00
	endTime := time.Date(2024, 10, 11, 18, 45, 0, 0, timeZone)  // 15:45

	// 定义总充电量
	totalEnergyKWh := 200.0 // 总充电量为10 kWh

	// 处理账单生成
	chargingStation.processBilling("VEHICLE123", startTime, endTime, totalEnergyKWh)
}